package com.coding.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.coding.entity.LiveMessage;
import com.coding.utils.Constant;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Cynicism
 * @version 1.0
 * @project DyApp
 * @description WebSocket会话管理器，负责管理所有已建立的WebSocket会话，并实现消息的广播。
 * @date 2024/7/9 10:08:44
 */
@Component
@Slf4j
public class LiveWebSocketHandler extends TextWebSocketHandler {

    /**
     * 使用CopyOnWriteArrayList存储WebSocket会话，确保线程安全。
     * 在连接建立后和连接关闭时，分别添加和移除会话。
     * 在广播消息时，遍历所有会话进行消息发送。
     */
    @Getter
    private static final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    // redis模板
    @Resource
    private RedisTemplate<String, String> MyRedisTemplate;

    /**
     * 在WebSocket连接建立后，将新建立的会话添加到会话列表中。
     *
     * @param session 新建立的WebSocket会话
     * @throws Exception 如果添加会话时发生异常
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String roomId = getRoomId(session);
        sessions.put(roomId, session);
        log.info("连接已建立，当前人数：{}", sessions.size());
    }

    /**
     * 在WebSocket连接关闭后，从会话列表中移除已关闭的会话。
     *
     * @param session 刚刚关闭的WebSocket会话
     * @param status  连接关闭状态
     * @throws Exception 如果移除会话时发生异常
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String roomId = getRoomId(session);
        sessions.remove(roomId);
        log.info("用户已离开直播间，当前人数：{}", sessions.size());
    }

    /**
     * 广播消息到所有已建立的WebSocket会话。
     * 如果在发送消息时发生IOException，该方法将打印异常堆栈跟踪并记录错误信息。
     *
     * @param message 要广播的消息
     */
    public static void broadcast(String sendRoomId, String message) {
        log.info("广播消息 开始------{}-------",sessions.values());

        sessions.values().stream()
                .filter(session -> sendRoomId.equals(session.getAttributes().get("roomId")))
                .forEach(session -> {
                    try {
                        session.sendMessage(new TextMessage(message));
                        log.info(" 接收消息的直播间：{}， 发送消息{}",sendRoomId, message);
                    } catch (Exception e) {
                        log.error(" like broadcast error: {}", e.getMessage());
                    }
                });
        log.info("广播消息 结束------------------------------------");
    }

    /**
     * 接收
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String userId = getUserId(session);
        log.info("收到消息 {}: {}", userId, message.getPayload());
        try {

            if (message.getPayload().equals("ping")) {
                session.sendMessage(new TextMessage("pong"));
                return;
            }

            LiveMessage msg = JSON.parseObject(message.getPayload(), LiveMessage.class);

            broadcast(String.valueOf(msg.getRoomId()),message.getPayload());

            // 保存消息
            saveComment(String.valueOf(msg.getRoomId()), userId, msg.getContent());

        } catch (JSONException e) {
            session.sendMessage(new TextMessage("无效的邮件格式"));
            log.error("Invalid JSON format: {}", e.getMessage());
        }
    }


    private static String getUserId(WebSocketSession session) {
        // 从session中获取用户id
        return (String) session.getAttributes().get("userId");
    }

    private static String getRoomId(WebSocketSession session) {
        // 从session中获取用户id
        return (String) session.getAttributes().get("roomId");
    }

    public void saveComment(String roomId, String userId, String comment) {
        String key = Constant.REDIS_COMMENT_KEY_PREFIX + roomId;
        String value = userId + ": " + comment;
        MyRedisTemplate.opsForList().rightPush(key, value);
        log.info("保存评论: {}, {}", key, value);
    }

}
